"""
This module if the main module of the mediamanager. It declares the class
which is used for all file operations.

:Author: Sven Walter <sven.walter@wltr.eu>
:Since: 02/08/2011
"""

from urlparse import urljoin
import os.path
import shutil
import glob
import hashlib
import time
from PIL import Image #@UnresolvedImport


from django import template
from django.core.cache import cache


from wltrweb.mediamanager import settings
from compress_script import compress_script
from compress_style import compress_style
from ..models import External


class _CacheDoesntExists(Exception):
    """ For MediaManger.cached() function."""
    pass


CACHE_NAME = 'wltrweb.apps.mediamanager-cache'
CACHE_EXPIRE = 60*60*24*7



def get_extension(path):
    """ Returns the file extension of given name/url/file. """
    ext = os.path.splitext(path)[1]
    if len(ext) > 4:
        return ''
    else:
        return ext


def generate_text_resource(source, target, kind):
    tfp = open(target, 'wb')

    if kind == 'style':
        compressor = compress_style
    elif kind == 'script':
        compressor = compress_script
    else:
        raise AttributeError('Type must be "style" or "script".')
    
    files = list(glob.iglob(source))
    files.sort()
    
    for file_name in files:
        sfp = open(file_name, "r")
        contents = sfp.read()
        tpl = template.loader.get_template_from_string(contents)
        contents = unicode(tpl.render(template.Context()))
        contents = compressor(contents).encode("utf-8")
        tfp.write(contents)
        sfp.close()
    

class MediaManager(object):
    """
    This class is the main class of the media manager. One intance of this
    class copies the assigned resource the cache directory and provides the
    URL to the file.
    """
    
    def __init__(self, ressource, size=None, crop=None):
        """
        The __init__ methode copies the assigned resource to the cache.
        
        If ressource is an URL, the file is downloaded to the temp directory. If
        the type is 'style' or 'script' the wildcards in the resource string
        will be expanded and merged into one file. After that this file gets
        compressed and will be moved into the cache directory.
        """
        
        self.extension = get_extension(ressource)
        self.size = size
        self.crop = crop
        
        if self.extension == '.css':
            kind = 'style'
        elif self.extension == '.js':
            kind = 'script'
        else:
            kind = 'image'
        
        if ressource.startswith(u'external://'):
            name = ressource[11:]
            ressource = External.objects.get(name=name).path
        
        if ressource.startswith(u'/') is not True:
            ressource = os.path.join(settings.SOURCE, ressource)
        
        key = u"%s_%s" % (ressource, self.size) # Key for the cache var.
        self.hash = "".join(i for i in key if ord(i)<128) # remove non-ascii chars (HACK)
        self.hash = hashlib.sha1(u"%s%s"%(self.hash, time.time())).hexdigest()
        self.resource = ressource
        
        if kind in ('style', 'script'):
            generate_text_resource(ressource, self.get_public_path(), kind)
            
        elif kind == 'image':
            shutil.copy(ressource, self.get_public_path())
            os.chmod(self.get_public_path(), 0644)
            
            if self.crop is not None:
                x,y,w,h = self.crop
                im = Image.open(self.get_public_path())
                box = im.crop((x,y,x+w,y+h))
                try:
                    box.save(self.get_public_path())
                except KeyError:
                    box.save(self.get_public_path(), "jpeg")

            if self.size is not None:
                im = Image.open(self.get_public_path())
                im.thumbnail(self.size, Image.ANTIALIAS)
                try:
                    im.save(self.get_public_path())
                except KeyError:
                    im.save(self.get_public_path(), "jpeg")
            
        

        os.chmod(self.get_public_path(), 0644)
        
        
    def get_public_filename(self):
        return self.hash + self.extension
    
    def get_public_path(self):
        return os.path.join(settings.CACHE, self.get_public_filename())
    
    def get_public_exists(self):
        return os.path.exists(self.get_public_path())
        
    def get_url(self):
        """ Returns the URL of the file in the cache. """
        return urljoin(settings.URL, self.get_public_filename())
    
    def get_is_same_file(self, path):
        return False
        

    @staticmethod
    def cached(ressource, size=None, crop=None):
        """ This static method checks the cache var if the ressource already
        exists else it creates it and save it to the cache var. """
        
        key = "%s_%s_%s" % (ressource, size, crop) # Key for the cache var.
        
        def get_cache(): return cache.get(CACHE_NAME) or {}
        def set_cache(cache_): cache.set(CACHE_NAME, cache_, CACHE_EXPIRE)
        

        try:
            if settings.DONT_CACHE:
                raise _CacheDoesntExists
            
            cache_ = get_cache()
            if key not in cache_:
                raise _CacheDoesntExists
            
            manager = cache_[key]
            
            if not manager.get_public_exists():
                cache_ = get_cache()
                del cache_[key]
                set_cache(cache_)
                raise _CacheDoesntExists            
            
        except _CacheDoesntExists:
            manager = MediaManager(ressource, size, crop)
            cache_ = get_cache()
            cache_[key] = manager
            set_cache(cache_)
                    
        return manager

